import subprocess

from flask import request, Blueprint, render_template

from zz import zz_code, zz_redis, zz_return, zz_str, zz_log, zz_siyoumi, zz_dingtalk

app = Blueprint('route_app', __name__)

log = zz_log.of()


# 控制台接口
@app.before_request
def before_request():
    token = request.headers.get("token")
    if zz_str.is_null_or_empty(token):
        token = request.args.get("token")

    log.info(f"token: {token}")
    if zz_str.is_null_or_empty(token):
        return zz_return.of(20015, "miss token").to_json()

    long_token = zz_code.env("LONG_TOKEN")
    if long_token == token:
        # 长期token
        pass
    else:
        redis = zz_redis.of()
        if not redis.get_client().exists(f"console:{token}"):
            return zz_return.of(20025, "token 异常").to_json()


cmd_key = 'console'


@app.route('/console.html', methods=['GET'])
def console():
    root = f"{request.scheme}://{request.host}"
    token = request.args.get("token")
    data = {
        "app_root": root + "/",
        "api_token": token,
    }
    return render_template("console.html", **data)


@app.route('/api/cmd', methods=['GET', 'POST'])
def cmd():
    script_index = zz_str.to_int(request.args.get("type"), 0)
    scripts = zz_code.env("SCRIPT").split(";")
    if script_index > len(scripts) - 1:
        return zz_return.of(20029, "type异常").to_json()

    cmd_str = scripts[script_index]
    key = cmd_run(cmd_str)

    r = zz_return.of(0)
    r.set_data("key", key)
    return r.to_json()


@app.route('/api/cmd_msg', methods=['GET'])
def cmd_msg():
    key = request.args.get("key")
    if zz_str.is_null_or_empty(key):
        return zz_return.of(20064, "miss key").to_json()

    log.info(f"key: {key}")
    redis = zz_redis.of()
    if not redis.exists(key):
        return zz_return.of(20074, "key error").to_json()

    done = False
    msg_arr = []
    while True:
        msg_byte = redis.client.rpop(cmd_key)
        msg = zz_str.bytes_to_str(msg_byte)
        if msg == None:
            break
        if msg == "done":
            done = True
            break

        msg_arr.append(msg)

    r = zz_return.of(0)
    r.set_data("msg", msg_arr)
    r.set_data("done", done)
    return r.to_json()


@app.route('/api/run_cmd_siyoumi', methods=['GET'])
def cmd_run_by_siyoumi():
    """
    来自siyoumi服务器命令
    """
    lock_key = "x|run_cmd_siyoumi"
    redis = zz_redis.of()
    r = redis.try_lock_fun(lock_key, func_cmd_siyoumi)
    return r.to_json()


def func_cmd_siyoumi() -> zz_return.Return:
    api = zz_siyoumi.of()
    r = api.find_cmd()
    if r.err():
        return r

    task_id = r.get_data("id")
    cmd_str = r.get_data("cmd")
    api.set_run(task_id)

    key = cmd_run(cmd_str, False)
    redis = zz_redis.of()
    msg_arr = []
    while True:
        msg_byte = redis.client.rpop(cmd_key)
        msg = zz_str.bytes_to_str(msg_byte)
        if msg is None:
            break
        if msg == "done":
            break
        msg_arr.append(msg)

    r = zz_return.of(0)
    r.set_data("msg", msg_arr)

    # 删除，通知已完成
    redis.get_client().delete(task_id)
    api.set_done(task_id, r.to_json())
    return r


def cmd_str_to_list(cmd: str):
    """
    ping baidu.com 变数组 ('ping','baidu.com')
    :param cmd:
    """
    cmd_arr = cmd.split(" ")
    arr = []
    for s in cmd_arr:
        if zz_str.is_null_or_empty(s):
            continue
        arr.append(s)
    return arr


def cmd_process(process):
    """
    # 命令反馈
    :param process:
    :return:
    """
    list_msg = []
    redis = zz_redis.of()
    for line in process.stdout:
        msg = line.replace("\n", '')
        redis.client.lpush(cmd_key, msg)
        log.info(msg)
        list_msg.append(msg)
    redis.client.lpush(cmd_key, "done")
    log.info("CMD FINAL 发钉钉")

    server = zz_code.env("SERVER", "dev")
    send_msg = f"**命令执行** [{server}]\n\n {zz_code.date_tostring()} \n\n"
    for msg in list_msg[0:8]:
        send_msg += msg + "\n\n"
        pass
    send_msg += "**命令执行完成** "

    zz_dingtalk.send_markdown(send_msg, "控制台")

    # 等待进程结束
    process.wait()


def cmd_run(cmd_str, do_thread=True) -> str:
    """
    运行某个脚本，返回redis key
    @param cmd_str: 脚本
    @param do_thread: true:开线程运行，不阻塞主线程; false:阻塞待
    """
    cmd_list = cmd_str_to_list(cmd_str)
    log.info(f"CMD: {cmd_str}")
    log.info(f"CMD_LIST: {cmd_list}")
    # 凭证，命令反馈用
    key = f"cmd:{zz_code.get_uuid()}"
    redis = zz_redis.of()
    # 删除旧key
    redis.get_client().delete(cmd_key)
    redis.delete_by_prefix("cmd:")
    #
    redis.get_client().setex(key, 60 * 10, "1")

    log.info("CMD BEGIN")
    try:
        process = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
        if do_thread:
            zz_code.thread_func(cmd_process, process)
        else:
            cmd_process(process)
    except Exception as ex:
        log.error(f"CMD ERROR：{ex}")
        redis.client.lpush(cmd_key, f"{ex}")
        redis.client.lpush(cmd_key, "done")
    log.info("CMD END")

    return key
